package com.xyzwps.orm.exposed.repository

import com.xyzwps.orm.exception.TagDuplicateException
import com.xyzwps.orm.model.Tag
import com.xyzwps.orm.repository.TagRepository
import com.xyzwps.orm.util.ExceptionUtils
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
import java.lang.IllegalStateException
import java.sql.SQLIntegrityConstraintViolationException
import java.util.*

class DslTagRepository(private val db: Database) : TagRepository {

    override fun insert(tag: Tag): Int {
        try {
            return transaction(db) {
                TagTable.insertAndGetId {
                    it[tagName] = tag.tagName
                    it[userId] = tag.userId
                }
            }.value
        } catch (ex: Exception) {
            if (ExceptionUtils.causedBy(ex, SQLIntegrityConstraintViolationException::class.java)) {
                throw TagDuplicateException(tag.tagName)
            } else {
                throw IllegalStateException(ex)
            }
        }
    }

    override fun update(tag: Tag) {
        try {
            transaction(db) {
                TagTable.update({ TagTable.id eq tag.tagId }) {
                    it[tagName] = tag.tagName
                    it[userId] = tag.userId
                }
            }
        } catch (ex: Exception) {
            if (ExceptionUtils.causedBy(ex, SQLIntegrityConstraintViolationException::class.java)) {
                throw TagDuplicateException(tag.tagName)
            } else {
                throw IllegalStateException(ex)
            }
        }
    }

    override fun delete(tagId: Int) {
        transaction(db) {
            TagTable.deleteWhere { TagTable.id eq tagId }
        }
    }

    override fun selectByTagId(tagId: Int): Optional<Tag> {
        return transaction(db) {
            val tag = TagTable.select { TagTable.id eq tagId }.map(::rowToTag).firstOrNull()
            Optional.ofNullable(tag)
        }
    }

    override fun selectAllForUser(userId: Int): List<Tag> = transaction(db) {
        TagTable.select { TagTable.userId eq userId }.map(::rowToTag).toList()
    }

    private fun rowToTag(row: ResultRow): Tag {
        val tag = Tag()
        tag.tagId = row[TagTable.id].value
        tag.tagName = row[TagTable.tagName]
        tag.userId = row[TagTable.userId]
        return tag
    }
}